﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;

namespace WakeOnHan
{
    public partial class frmWOH : Form
    {
        static string currentFolder = Path.GetDirectoryName(Application.ExecutablePath);
        static string addressList = currentFolder + "\\" + "WOH.xml";

        // workstations contains all workstations from xml.
        Dictionary<string, Workstation> workstations = new Dictionary<string, Workstation>();
        DataTable WorkstationsToWakeUp = new DataTable();

        public frmWOH()
        {
            InitializeComponent();
            loadWorkstations();
            initializeTable();
            getWorkstationFromArguments();
        }

        private void initializeTable()
        {
            WorkstationsToWakeUp.Columns.Add("Workstation", typeof(string));
            WorkstationsToWakeUp.Columns.Add("Result", typeof(string));

            // Bind the table to datagridview.
            dgWakeUp.DataSource = WorkstationsToWakeUp;
            dgWakeUp.Columns[0].Width = 80;
            dgWakeUp.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
        }

        public void addWorkstationToTable(string workstation)
        {
            WorkstationsToWakeUp.Rows.Add(workstation);
        }

        public void clearWorkstationTable()
        {
            WorkstationsToWakeUp.Clear();
        }

        private void getWorkstationFromArguments()
        {
            string[] args = Environment.GetCommandLineArgs();

            for (int i = 0; i < args.Length; i++)
            {
                // args[0] is exepath, daarna komen eventuele echte argumenten.
                if (i == 1)
                {
                    addWorkstationToTable(args[i]);
                }
            }
        }

        private void loadWorkstations()
        {
            try
            {
                using (XmlReader xmlReader = XmlReader.Create(addressList))
                {
                    while (xmlReader.Read())
                    {
                        if ((xmlReader.NodeType == XmlNodeType.Element) && (xmlReader.Name == "address"))
                        {
                            if (xmlReader.HasAttributes)
                            {
                                string hostname = xmlReader.GetAttribute("hostname");
                                string ip = xmlReader.GetAttribute("ip");
                                string mac = xmlReader.GetAttribute("mac");
                                string subnetmask = xmlReader.GetAttribute("subnetmask");
                                Workstation ws = new Workstation(hostname, ip, mac, subnetmask);
                                if (!workstations.ContainsKey(hostname))
                                {
                                    workstations.Add(hostname, ws);
                                }
                            }
                        }
                    }
                    xmlReader.Close();
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }

        private void btnWakeUp_Click(object sender, EventArgs e)
        {
            string result = "";
            foreach (DataRow row in WorkstationsToWakeUp.Rows) // Loop over the rows.
            {
                result = wakeup(row["Workstation"].ToString());
                // Write result to table
                row["Result"] = result;
            }
        }

        string wakeup(string workstation)
        {
            string result = "";
            if (workstation.Length > 0)
            {
                // List<string> hits = workstations.Keys.Where(key => key.Contains(workstation)).ToList();
                List<string> hits = workstations.Keys.Where
                    (key => key.StartsWith(workstation, StringComparison.OrdinalIgnoreCase)).ToList();
                if (hits.Count > 0)
                {
                    if (hits.Count == 1)
                    {
                        Workstation ws = workstations[hits[0]];
                        try
                        {
                            ws.WakeUp();
                            // result = "Wake Up pakket verzonden naar broadcast adres: " + ws.BroadcastAddress;
                            result = "Wake Up pakket verzonden naar: " + ws.Name;
                        }
                        catch (Exception err)
                        {
                            MessageBox.Show(err.Message);
                        }
                    }
                    else
                    {
                        result = workstation + " komt " + hits.Count.ToString() + " keer voor in de lijst.";
                    }
                }
                else
                {
                    result = workstation + " niet gevonden.";
                }
            }
            // else do nothing result = "";
            return result;
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            frmAbout about = new frmAbout();
            about.Show();
        }

        private void btnAdComputers_Click(object sender, EventArgs e)
        {
            // Create a new addComputers form and add a reference to the
            // mainform.
            frmAddComputers addComputers = new frmAddComputers(this);
            addComputers.Show();
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            WorkstationsToWakeUp.Clear();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void mergeAddressLists(string mainList, string updateList)
        {
            try
            {
                XmlTextReader xmlreader1 = new XmlTextReader(mainList);
                XmlTextReader xmlreader2 = new XmlTextReader(updateList);

                // Both datasets contain a "address" table
                DataSet ds = new DataSet();
                ds.ReadXml(xmlreader1);
                DataSet ds2 = new DataSet();
                ds2.ReadXml(xmlreader2);

                xmlreader1.Close();
                xmlreader2.Close();

                ds.Tables["address"].PrimaryKey = new DataColumn[] { ds.Tables["address"].Columns["hostname"] };

                ds.Merge(ds2);

                // save updated xml file
                ds.WriteXml(addressList);
                MessageBox.Show("Completed merging XML documents");
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void loadAddressList()
        {
            workstations.Clear();
            loadWorkstations();
        }

        private void MergeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Show the openfiles dialog.
            string[] updateFileList = { };
            DialogResult result = openFileDialog1.ShowDialog();

            if (result == DialogResult.OK) // Test result.
            {
                updateFileList = openFileDialog1.FileNames;
                foreach (string file in updateFileList)
                {
                    mergeAddressLists(addressList, file);
                }
            }
            loadAddressList();
        }
      }

    // Class workstation
    public class Workstation
    {
        private string name;
        private System.Net.IPAddress ip;
        private string mac;
        private System.Net.IPAddress subnetMask;
        private System.Net.IPAddress broadcastAddress;

        public Workstation(string WSname, string ipAdress, string macAddress, string subnetmask)
        {
            Name = WSname;
            Ip = ipAdress;
            Mac = macAddress;
            Subnetmask = subnetmask;
            broadcastAddress = GetBroadcastAddress(ip, subnetMask);
        }

        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

        public string Ip
        {
            get
            {
                return ip.ToString();
            }
            set
            {
                ip = System.Net.IPAddress.Parse(value);
            }
        }

        public string Mac
        {
            get
            {
                return mac;
            }
            set
            {
                mac = value;
            }
        }

        public string Subnetmask
        {
            get
            {
                return subnetMask.ToString();
            }
            set
            {
                subnetMask = IPAddress.Parse(value);
            }
        }

        public IPAddress BroadcastAddress
        {
            get
            {
                return broadcastAddress;
            }
        }

        //All you need todo is call the function with a valid MAC address and
        //a broadcast ip address. //MAC_ADDRESS should  look like '013FA049'
        //In most cases 255.255.255.255 will work.
        //However if you want to broadcast across a certain remote network
        //on another subnet you may need to user X.X.255.255 for example.
        //Otherwise the packet will not be routed to the correct network.

        public void WakeUp()
        {
            System.Net.Sockets.UdpClient client = new System.Net.Sockets.UdpClient();

            Byte[] datagram = new byte[102];

            for (int i = 0; i <= 5; i++)
            {
                datagram[i] = 0xff;
            }

            string[] macDigits = null;
            if (Mac.Contains("-"))
            {
                macDigits = Mac.Split('-');
            }
            else
            {
                macDigits = Mac.Split(':');
            }

            if (macDigits.Length != 6)
            {
                throw new ArgumentException("Incorrect MAC address supplied!");
            }

            int start = 6;
            for (int i = 0; i < 16; i++)
            {
                for (int x = 0; x < 6; x++)
                {
                    datagram[start + i * 6 + x] = (byte)Convert.ToInt32(macDigits[x], 16);
                }
            }

            // IPAddress address = IPAddress.Parse(ipAddress);
            // IPAddress mask = IPAddress.Parse(subnetMask);
            // IPAddress broadcastAddress = ip.GetBroadcastAddress(mask);

            client.Send(datagram, datagram.Length, broadcastAddress.ToString(), 7);
        }

        // Calculates the broadcastaddress based on ip address and subnetmask
        private IPAddress GetBroadcastAddress(IPAddress ipAddress, IPAddress mask)
        {
            //determines a broadcast address from an ip and subnet
            // var ip = IPAddress.Parse(ipAddress);
            // var mask = IPAddress.Parse(subnetMask);

            byte[] ipAdressBytes = ipAddress.GetAddressBytes();
            byte[] subnetMaskBytes = mask.GetAddressBytes();

            if (ipAdressBytes.Length != subnetMaskBytes.Length)
                throw new ArgumentException("Lengths of IP address and subnet mask do not match.");

            byte[] broadcastAddr = new byte[ipAdressBytes.Length];
            for (int i = 0; i < broadcastAddr.Length; i++)
            {
                broadcastAddr[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
            }
            return new IPAddress(broadcastAddr);
        }
    }
}
